<?php
// +----------------------------------------------------------------------
// | EpisodeAllocation 类型服务
// +----------------------------------------------------------------------
// | 主要服务于EpisodeAllocation数据处理
// +----------------------------------------------------------------------
// | 错误编码头 208xxx
// +----------------------------------------------------------------------

namespace common\service;


use common\model\ModuleModel;
use common\model\ProjectModel;
use common\model\StatusModel;
use support\ErrorCode;
use support\SingletonTrait;
use think\module\Module;
use common\model\EntityModel;
use common\model\EpisodeAllocationModel;
use common\model\UserModel;

class EpisodeAllocationService
{

    private $episodeAllocationModel;

    // 单例工具
    use SingletonTrait;

    public function __construct()
    {
        $this->episodeAllocationModel = model(EpisodeAllocationModel::class);
    }

    /**
     * 查询剧集 关联剧集分配信息
     * @param $param
     * @return array
     */
    public function selectEpisodeWithAllocation($param)
    {
        $moduleData = Module::$moduleDictData['module_index_by_code']['episode'];

        $entityModel = model(EntityModel::class);

        $page = $param['param']['page'] ?? [1, C("database.database_max_select_rows")];
        $where = [
            "module_id" => $moduleData['id'],
            'project_id' => $param['param']['filter']['project_id']
        ];

        $fields = ["id", "name", "code",
            "json_unquote(json_extract(json, '$.episode_is_seal')) as episode_is_seal",
            "json_unquote(json_extract(json, '$.episode_plan_seal_time')) as episode_plan_seal_time",
            "json_unquote(json_extract(json, '$.episode_seal_time')) as episode_seal_time",
            "json_unquote(json_extract(json, '$.episode_is_show')) as episode_is_show",
            "json_unquote(json_extract(json, '$.episode_plan_show_time')) as episode_plan_show_time",
            "json_unquote(json_extract(json, '$.episode_show_time')) as episode_show_time",
            "json_unquote(json_extract(json, '$.episode_grade')) as episode_grade",
            "json_unquote(json_extract(json, '$.episode_custom_grade')) as episode_custom_grade",
        ];

        $rows = $entityModel
            ->where($where)
            ->field($fields)
            ->page($page[0], $page[1])
            ->order("LENGTH(code) asc,code asc")
            ->select();


        $episodeIds = [];
        foreach ($rows as $row) {
            $episodeIds[$row['id']] = $row['id'];
        }
        //被分配的用户
        $episodeAllocationList = model(EpisodeAllocationModel::class)->where(['entity_id' => ['IN', join(',', $episodeIds)]])->field('entity_id,role_code,user_id')->select();

        $userIds = [];
        foreach ($episodeAllocationList as $episodeAllocationListItem) {
            if ($episodeAllocationListItem['user_id'] > 0) {
                $userIds[$episodeAllocationListItem['user_id']] = $episodeAllocationListItem['user_id'];
            }
        }

        $allocationUsers = [];
        if (!empty($userIds)) {
            $userModel = model(UserModel::class);
            $allocationUsers = $userModel->where(['id' => ['IN', implode(',', $userIds)]])->field("id,avatar,name,phone,email,union_id")->select();
            $allocationUsers = array_column($allocationUsers, null, 'id');
        }

        $episodeAllocationMap = [];
        foreach ($episodeAllocationList as $episodeAllocationItem) {
            if (!isset($episodeAllocationMap[$episodeAllocationItem['entity_id']][$episodeAllocationItem['role_code']]) && isset($allocationUsers[$episodeAllocationItem['user_id']])) {
                $episodeAllocationMap[$episodeAllocationItem['entity_id']][$episodeAllocationItem['role_code']] = $allocationUsers[$episodeAllocationItem['user_id']];
            }
        }

        $rows = array_map(function ($row) use ($episodeAllocationMap) {
            if (isset($episodeAllocationMap[$row['id']])) {
                $row = array_merge($row, $episodeAllocationMap[$row['id']]);
            }
            $row['episode_plan_seal_time'] = (int)$row['episode_plan_seal_time'];
            $row['episode_seal_time'] = (int)$row['episode_seal_time'];
            $row['episode_plan_show_time'] = (int)$row['episode_plan_show_time'];
            $row['episode_show_time'] = (int)$row['episode_show_time'];
            return $row;
        }, $rows);

        // 合并数据 分离不同角色用户信息
        $total = $entityModel->where($where)->count();
        return ["total" => $total, "rows" => $rows];
    }

    /**
     * 分配剧集
     * @param array $data
     * @param int $tenantId
     * @return array
     * @throws \Exception
     */
    public function allocationEpisode(array $data, int $tenantId)
    {
        $episodeAllocationModel = model(EpisodeAllocationModel::class);
        $entityIds = $data['entity_ids'];
        $entityIdArray = explode(',', $entityIds);
        $episodeAllocation = $episodeAllocationModel
            ->where([
                'entity_id' => ["IN", $entityIds],
                'role_code' => $data['role_code'],
            ])
            ->field("id,entity_id")
            ->select();

        $existEntityIdArray = array_column($episodeAllocation, 'entity_id');
        $episodeAllocation = array_column($episodeAllocation, 'id', 'entity_id');

        $add = array_diff($entityIdArray, $existEntityIdArray);
        $data['user_id'] = $data['id'];
        $result = [];

        unset($data['entity_ids']);
        foreach ($entityIdArray as $entityId) {
            $data['entity_id'] = $entityId;
            $data['tenant_id'] = $tenantId;
            if (in_array($entityId, $add)) {
                unset($data['id']);
                $res = $episodeAllocationModel->addItem($data);
                if ($res === false) {
                    throw_strack_exception($episodeAllocationModel->getError(), ErrorCode::EPISODE_ALLOCATION_ADD_FAILURE);
                }
            } else {
                $data['id'] = $episodeAllocation[$entityId];
                $res = $episodeAllocationModel->modifyItem($data);
                if ($res === false && $episodeAllocationModel->getErrorCode() !== \think\exception\ErrorCode::NO_DATA_HAS_BEEN_CHANGED) {
                    throw_strack_exception($episodeAllocationModel->getError(), ErrorCode::EPISODE_ALLOCATION_UPDATE_FAILURE);
                }
            }
            $result[$entityId] = $res;
        }

        // 剧集分配的设置 也要同步到项目成员中
        if ($data['user_id'] > 0) {
            $projectService = ProjectService::getInstance();
            $data = [
                'user_list' => [
                    [
                        'role_code' => $data['role_code'],
                        'user_id' => $data['user_id'],
                    ]
                ],
                'project_id' => $data['project_id'],
            ];
            $projectService->batchCreateProjectMemberMultiRole($data);
        }

        return $result;
    }

    /**
     * 查询集的管理人
     * @param $filter
     * @return array|null
     */
    public function getEpisodeAllocation($filter)
    {
        return model(EpisodeAllocationModel::class)->where($filter)->find();
    }

    /**
     * 查询集的管理人
     * @param $filter
     * @return array|null
     */
    public function selectEpisodeAllocations($filter)
    {
        return model(EpisodeAllocationModel::class)->where($filter)->select();
    }

    /**
     * 用户项目剧集分配树
     * @param $param
     * @param int $tenantId
     * @param int $userId
     * @return array|mixed
     * @throws \Exception
     */
    public function selectProjectEpisodeAllocationTree($param, int $tenantId, int $userId)
    {
        $epaModel = model(EpisodeAllocationModel::class);
        $param['filter'] = append_filter_param($param, 'episode_allocation', [
            'user_id' => $userId,
            'tenant_id' => $tenantId,
        ]);
        $page = $param['page'] ?? [1, C("database.database_max_select_rows")];
        $order = $param['order'] ?? "id DESC";

        $epData = $epaModel->where($param['filter'])->page($page[0], $page[1])->order($order)->group("episode_allocation.entity_id")->select();

        if (count($epData) == 0) {
            return [];
        }

        $episodeAllocationGroupByProject = array_group_by($epData, 'project_id');
        $projectIdList = array_keys($episodeAllocationGroupByProject);

        $projectFilter = ['project.id' => ['IN', implode(',', $projectIdList)]];
        $statusFilter = $param['project_filter']['project_status'] ?? null;

        if (!empty($statusFilter)) {
            $statusCorrespond = ProjectService::getInstance()->filterByStatus($statusFilter);
            $statusData = model(StatusModel::class)->selectData([
                "filter" => ["correspond" => ["IN", $statusCorrespond]],
                "fields" => "id",
            ]);
            $statusData = array_column($statusData['rows'], 'id', 'id');
            $projectFilter['status.id'] = ['IN', implode(',', $statusData)];
        }

        $projectList = model(ProjectModel::class)
            ->join("`status` on JSON_UNQUOTE( JSON_EXTRACT( `project`.`json`, '$.project_status' ) ) = `status`.id")
            ->where($projectFilter)
            ->field("project.id,project.name,project.code,project.id as real_id")
            ->select();

        $entityIdList = array_column($epData, 'entity_id', 'entity_id');
        $entityList = model(EntityModel::class)->where(['id' => ['IN', implode(',', $entityIdList)]])->field("id,name,code,id as real_id")->select();
        $entityMap = array_column($entityList, null, 'id');
        foreach ($projectList as &$project) {
            $projectId = $project['id'];
            $project['id'] = "project_" . $projectId;

            $currentProjectEpisodeAllocationList = $episodeAllocationGroupByProject[$projectId] ?? [];
            foreach ($currentProjectEpisodeAllocationList as $episodeAllocation) {
                if (isset($entityMap[$episodeAllocation['entity_id']])) {
                    $project['children'][] = $entityMap[$episodeAllocation['entity_id']];
                }
            }
            $project['children'] = array_values(array_sort_by($project['children'] ?? [], 'code'));
        }

        return $projectList;
    }

    /**
     * 查询分配的实体
     * @param $filter
     * @param $tenantId
     * @param $userId
     * @return array|false|mixed|string
     */
    public function selectByUser($filter, $tenantId, $userId)
    {
        $filter[] = [
            'tenant_id' => $tenantId,
            'user_id' => $userId,
            'entity_id' => ['GT', 0]
        ];
        $list = $this->selectEpisodeAllocations($filter);
        $list = array_column($list, 'entity_id', 'entity_id');
        if (empty($list)) {
            return [];
        }
        if (count($list) === 0) {
            return [];
        }

        $entityFilter = [
            'tenant_id' => $tenantId,
            'project_id' => $filter['project_id'],
            'id' => ['IN', implode(',', $list)]
        ];
        return model(EntityModel::class)->where($entityFilter)->select();
    }

    /**
     * 获取指定集数下面实体ids
     * @param $currentUserId
     * @param array $projectIds
     * @return array
     */
    public function getEpisodesAllocationEntityIds($currentUserId, $projectIds = [])
    {
        $moduleId = model(ModuleModel::class)
            ->field('id,code')
            ->where(['code' => ["IN", "episode,sequence,shot"]])
            ->select();

        $moduleMap = array_column($moduleId, null, 'code');

        // 查询当前导演管控的集数
        $episodeAllocationModel = model(EpisodeAllocationModel::class);

        $epWhere = ['user_id' => $currentUserId];
        if (!empty($projectIds)) {
            $epWhere['project_id'] = ["in", implode(',', $projectIds)];
        }
        $episodesEntityData = $episodeAllocationModel->field('id')->where($epWhere)->select();
        $episodesEntityIds = array_column($episodesEntityData, 'id');
        $sequenceEntityIds = [];
        $shotEntityIds = [];
        if (!empty($episodesEntityIds)) {

            // 当前序列
            $entityModel = model(EntityModel::class);
            $sequenceEntityData = $entityModel->field('id')->where([
                'module_id' => $moduleMap['sequence']['id'],
                'entity_id' => ['IN', join(',', $episodesEntityIds)]
            ])->select();
            $sequenceEntityIds = array_column($sequenceEntityData, 'id');

            if (!empty($sequenceEntityIds)) {
                // 当前镜头
                $shotEntityData = $entityModel->field('id')->where([
                    'module_id' => $moduleMap['shot']['id'],
                    'entity_id' => ['IN', join(',', $sequenceEntityIds)]
                ])->select();
                $shotEntityIds = array_column($shotEntityData, 'id');
            }
        }


        return [
            'episode' => [
                'module_id' => $moduleMap['episode']['id'],
                'ids' => $episodesEntityIds
            ],
            'sequence' => [
                'module_id' => $moduleMap['sequence']['id'],
                'ids' => $sequenceEntityIds
            ],
            'shot' => [
                'module_id' => $moduleMap['shot']['id'],
                'ids' => $shotEntityIds
            ]
        ];
    }

    /**
     * 查询一集的剧集分配情况
     * @param $episodeId
     * @return array
     */
    public function getEpisodeAllocationData($episodeId)
    {
        $episodeAllocationList = $this->episodeAllocationModel->where(['entity_id' => $episodeId])->select();
        return array_group_by($episodeAllocationList, 'role_code');
    }

    /**
     * 查询当前用户被分配的剧集
     * @param $filter
     * @return array
     */
    public function selectCurrentUserByRoleCode($filter)
    {
        $episodeAllocationList = model(EpisodeAllocationModel::class)->where($filter)->select();
        if (empty($episodeAllocationList)) {
            return null;
        }
        $episodeIdList = array_column($episodeAllocationList, 'entity_id', 'entity_id');
        $episodeList = model(EntityModel::class)->where(['id' => ['IN', array_values($episodeIdList)]])->field('id,name,code')->select();
        $episodeList = array_column($episodeList, null, 'id');
        $episodeAllocationListGrouped = array_group_by($episodeAllocationList, 'role_code');
        foreach ($episodeAllocationListGrouped as &$episodeAllocationList) {
            $tmpEpisodeCodeList = [];
            foreach ($episodeAllocationList as $episodeAllocation) {
                if (isset($episodeList[$episodeAllocation['entity_id']])
                    && !in_array($episodeList[$episodeAllocation['entity_id']], $tmpEpisodeCodeList)) {
                    $tmpEpisodeCodeList[] = $episodeList[$episodeAllocation['entity_id']];
                }
            }
            $episodeAllocationList = $tmpEpisodeCodeList;
        }
        return $episodeAllocationListGrouped;
    }

    /**
     * 根据项目查找剧集进度
     * @param $param
     * @param $tenantId
     * @return array
     * @throws \think\Exception
     */
    public function selectEpisodeScheduleByProject($param, $tenantId)
    {
        $page = $param['page'] ?? [1, C("database.database_max_select_rows")];
        $episodeModule = module_by_code("episode");
        $filter = [
            "project_id" => $param['filter']['project_id'],
            'module_id' => $episodeModule['id'],
            'code' => ['LIKE', "EP%"],
            'LENGTH(name)' => 5,
            "_string" => "json->>'$.episode_is_delete' is null or json->>'$.episode_is_delete' = 'no'"
        ];
        $fields = "id,code,name,project_id,module_id,tenant_id,json_unquote(json_extract(json, '$.episode_is_show')) as episode_is_show,json_unquote(json_extract(json, '$.episode_is_seal')) as episode_is_seal,json_unquote(json_extract(json, '$.episode_status')) as episode_status";
        $res["list"] = model(EntityModel::class)
            ->field($fields)
            ->where($filter)
            ->page($page[0], $page[1])
            ->order("id asc")
            ->select();
        if (empty($res["list"])) {
            return [];
        }

        $statusData = model(StatusModel::class)->where(['code' => ['IN', 'final']])->field('id,code')->select();
        $statusMap = array_column($statusData, null, 'code');
        // 进入成片阶段集
        $episodeData = model(EntityModel::class)
            ->where($filter)
            ->where("json_unquote(json_extract(json, '$.episode_status')) = {$statusMap['final']['id']}")
            ->field("name,json_unquote(json_extract(json, '$.episode_is_seal')) as episode_is_seal")
            ->select();

        $episodeData = array_filter($episodeData, function ($tmp) {
            return $tmp['episode_is_seal'] != 'yes';
        });
        $finalVideoEpisode = [];
        if (!empty($episodeData)) {
            $finalVideoEpisode = array_column($episodeData, 'name');
        }

        // 查询进度
        $episodeScheduleData = model(EntityModel::class)
            ->where(['project_id' => $param['filter']['project_id'], 'module_id' => module_by_code('storyboard')['id'], 'tenant_id' => $tenantId])
            ->field("initial_episode_code,count(*) as all_num,count(json_unquote(json_extract(json, '$.storyboard_status')) = {$statusMap['final']['id']} or null) as final_status_num")
            ->group('initial_episode_code')
            ->select();

        $episodeScheduleMap = array_column($episodeScheduleData, null, 'initial_episode_code');

        $res['list'] = array_map(function ($item) use ($finalVideoEpisode, $episodeScheduleMap) {
            // 先判断是否播出或者封板
            if ($item["episode_is_show"] == 'yes') {
                $item['status'] = 'already_show'; //  already_show：已播出
            } else if ($item['episode_is_seal'] == 'yes') {
                $item['status'] = 'already_seal'; //  already_seal：制作完成
            } else if ($item['episode_status'] != '' && $item['episode_status'] != '7' && $item['episode_status'] != '1') {
                $item['status'] = 'inprogress'; //   inprogress 制作中
            } else if ($item['episode_status'] == '' || $item['episode_status'] == '1') {
                $item['status'] = 'not_started'; //  not_started 未开始
            } else if (in_array($item['code'], $finalVideoEpisode)) {
                $item['status'] = 'final_video_phase'; // 成片阶段
            } else {
                $item['status'] = 'not_started'; //  not_started 未开始
            }
            $schedule = 0;
            $episodeScheduleFinalInfo = $episodeScheduleMap[$item['code']] ?? null;
            if (!empty($episodeScheduleFinalInfo)) {
                $schedule = 100;
                if (!empty($episodeScheduleFinalInfo['all_num'])) {
                    $schedule = ($episodeScheduleFinalInfo['final_status_num'] / $episodeScheduleFinalInfo['all_num']) * 100;
                }
            }
            $item['schedule'] = $schedule; // 进度 = 分镜完成数量 / 分镜总数
            return $item;
            // 前者没有就判断是否有状态
        }, $res['list']);

        if ((int)$page[0] == 1) {
            $sum = model(EntityModel::class)->where($filter)->field("
            count(id) as episode_create,
            count(json->>'$.episode_is_show'='yes' or null) as episode_show,
            count(json->>'$.episode_is_seal'='yes') as episode_seal,
            count((json->>'$.episode_is_seal'='yes' and json->>'$.episode_is_show'='yes') or null) as completed
            ")->find();

            $res["sum"]["episode_create"] = $sum['episode_create'];
            $res["sum"]["episode_show"] = $sum['episode_show'];
            $res["sum"]["episode_seal"] = $sum['episode_seal'];

            // 未开始
            $notStartedCount = 0;
            $notStartedTmp = model(EntityModel::class)
                ->where($filter)
                ->field("json_unquote(json_extract(json, '$.episode_status')) as episode_status")
                ->select();

            if (!empty($notStartedTmp)) {
                foreach ($notStartedTmp as $notStartedTmpItem) {
                    if ($notStartedTmpItem['episode_status'] == '' || $notStartedTmpItem['episode_status'] == '1') {
                        $notStartedCount++;
                    }
                }
            }

            $res['sum']['in_production'] = $res['sum']['episode_seal']; // 制作中
            $res['sum']['final_video_phase'] = count($episodeData); // 成片阶段
            $res['sum']['not_started'] = $notStartedCount; // 未开始
            $res['sum']['completed'] = $sum['completed']; // 已完成集数
        }

        $finalStatusGroup = ['already_seal', 'already_show']; // 已完成-组:制作完成+已播出
        $makeInStatusGroup = ['inprogress', 'final_video_phase']; // 制作中-组:成片阶段+制作阶段
        $notStartedStatusGroup = ['not_started']; // 未开始-组：未开始

        // 状态分组
        $res['list'] = array_map(function ($tmp) use ($finalStatusGroup, $makeInStatusGroup, $notStartedStatusGroup) {
            $groupStatus = null;
            if (in_array($tmp['status'], $finalStatusGroup)) {
                $groupStatus = 'final_status_group';
            }
            if (in_array($tmp['status'], $makeInStatusGroup)) {
                $groupStatus = 'make_in_status_group';
            }
            if (in_array($tmp['status'], $notStartedStatusGroup)) {
                $groupStatus = 'not_started_status_group';
            }
            $tmp['group_status'] = $groupStatus;
            return $tmp;
        }, $res['list']);
        return $res;
    }


    /**
     * 组织剧集场次查询sql
     * "initial_episode_code_list": [
     *    {
     *           "initial_episode_code": "EP012",
     *           "showings_number": [
     *           "SC001",
     *           "SC002",
     *           "SC002",
     *           "SC003"
     *           ]
     *    },
     *    {
     *           "initial_episode_code": "EP013",
     *           "showings_number": [
     *           "SC001"
     *           ]
     *     }
     *  ]
     * @param $initialEpisodeCodeList
     * @param $episodeKey
     * @param $showingsKey
     * @return string
     */
    public function getEpisodeShowingsSql($initialEpisodeCodeList, $episodeKey, $showingsKey)
    {

        if (empty($episodeKey) || empty($showingsKey)) {
            return '';
        }
        $sql = '(';
        foreach ($initialEpisodeCodeList as $item) {
            $sql .= "(`$episodeKey` = " . "'{$item[$episodeKey]}' ";
            $showingsNumberSql = '';
            if (!empty($item[$showingsKey])) {
                foreach ($item[$showingsKey] as $showingsNum) {
                    $showingsNumberSql .= "'" . $showingsNum . "',";
                }
                $showingsNumberSql = trim($showingsNumberSql, ',');
                $showingsNumberSql = "and `$showingsKey` IN ({$showingsNumberSql})";
                $sql .= $showingsNumberSql;
            }
            $sql .= ' ) or ';
        }
        $sql = trim($sql, 'or ');
        $sql .= ')';
        return $sql;
    }

}
